#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
#include <signal.h>

#include "data_link.h"

char cframe_set[] = { FLAG, ADDRESS, C_SET, BCC(C_SET), FLAG };
char cframe_ua[] = { FLAG, ADDRESS, C_UA, BCC(C_UA), FLAG };
char cframe_disc[] = { FLAG, ADDRESS, C_DISC, BCC(C_DISC), FLAG };
char cframe_rr_zero[] = { FLAG, ADDRESS, C_RR(0), BCC(C_RR(0)), FLAG };
char cframe_rr_one[] = { FLAG, ADDRESS, C_RR(1), BCC(C_RR(1)), FLAG };
char cframe_rej_zero[] = { FLAG, ADDRESS, C_REJ(0), BCC(C_REJ(0)), FLAG };
char cframe_rej_one[] = { FLAG, ADDRESS, C_REJ(1), BCC(C_REJ(1)), FLAG };
char dframe_header_zero[] = { FLAG, ADDRESS, C_S(0), BCC(C_S(0)) };
char dframe_header_one[] = { FLAG, ADDRESS, C_S(1), BCC(C_S(1)) };

int _alarm = 0;
int _flag;
int _rr = 0;

int time_out = TIME_OUT;
int max_timeouts = MAX_TIMEOUTS;

char * get_cframe_rr() {
	return (_rr == 0 ? cframe_rr_zero : cframe_rr_one);
}

char * next_cframe_rr() {
	return (_rr == 0 ? cframe_rr_one : cframe_rr_zero);
}

char * get_dframe_header() {
	return (_rr == 0 ? dframe_header_zero : dframe_header_one);
}

char * next_dframe_header() {
	return (_rr == 0 ? dframe_header_one : dframe_header_zero);
}

void alarmHandler(int signo) {
	_alarm = 1;
	
	signal(SIGALRM, alarmHandler);
}

int stuff_buffer(char* packet, char* stuffed_packet, int packet_length)
{
    int i, j;
    char c;
    
    for(i = 0, j = 0, c = packet[i]; i < packet_length; ++i, ++j, c = packet[i]) {
    	if(c == ESC) {
            stuffed_packet[j] = ESC;
            stuffed_packet[++j] = 0x5d;
        }
    	else if(c == FLAG) {
            stuffed_packet[j] = ESC;
            stuffed_packet[++j] = 0x5e;
        }
        else
            stuffed_packet[j] = c;
    }
    
    return j;
}

int destuff_buffer(char* stuffed_packet, char* destuffed_packet, int destuff_p_len)
{
    int i, j;
    
    for(i = 0, j = 0; i < destuff_p_len; ++i, ++j) {
        if(stuffed_packet[i] == ESC) {
            if(stuffed_packet[i+1] == 0x5e) {
                destuffed_packet[j] = FLAG;
                ++i;
            }
            else if(stuffed_packet[i+1] == 0x5d) {
                destuffed_packet[j] = ESC;
                ++i;
            }
        }
        else
            destuffed_packet[j] = stuffed_packet[i];
    }
   
    return j;
}

int open_connection(int port) {
	char str[32];
	
	_rr = 0;

	// Register alarm handler
	signal(SIGALRM, alarmHandler);

	if (port == TTYS0)
		strcpy(str, TTYS0_VALUE);
	else if (port == TTYS1)
		strcpy(str, TTYS1_VALUE);

	return open(str, O_RDWR | O_NOCTTY | O_NONBLOCK);
}

int write_frame_header(int fd, char control) {
	int bytes_written = 0;
	char c = FLAG;
	bytes_written += write(fd, &c, 1);
	#if DEBUG == 1
	printf("[%x|", c);
	#endif

	c = ADDRESS;
	bytes_written += write(fd, &c, 1);
	#if DEBUG == 1
	printf("%x|", c);
	#endif

	c = control;
	bytes_written += write(fd, &c, 1);
	#if DEBUG == 1
	printf("%x|", c);
	#endif

	c = ADDRESS ^ control;
	bytes_written += write(fd, &c, 1);
	#if DEBUG == 1
	printf("%x|", c);
	#endif
	
	return bytes_written;
}

void write_cframe(int fd, char control) {
	#if DEBUG == 1
	printf("\n---\nsending %x control frame\n", control);
	#endif
	
	write_frame_header(fd, control);

	char c = FLAG;
	write(fd, &c, 1);
	#if DEBUG == 1
	printf("%x]\n", c);
	printf("%x control frame sent\n---\n", control);
	#endif
}

int write_dframe(int fd, char *packet, int p_len) {
	int bytes_written = 0, i = 0;
	
	#if DEBUG == 1
	printf("\n---\nsending %x information frame\n", C_S(_rr));
	#endif
	
	write_frame_header(fd, C_S(_rr));
	
	char bcc2 = packet[0];
	for (i = 1; i < p_len; i++) {
		bcc2 ^= packet[i];
	}
	
	char buff_with_bcc2[p_len+1];
	memcpy(buff_with_bcc2, packet, p_len);
	buff_with_bcc2[p_len] = bcc2;
	
	char new_buf[MAX_FRAME_SIZE];
	bzero(new_buf, MAX_FRAME_SIZE);
	int stuff_len = stuff_buffer(buff_with_bcc2, new_buf, p_len+1);
	bytes_written += write(fd, &new_buf, stuff_len);
	
	#if DEBUG == 1
	printf("bcc2 = %x\n", bcc2);
	#endif

	char c = FLAG;
	write(fd, &c, 1);
	#if DEBUG == 1
	printf("%x information frame sent\n---\n", C_S(_rr));
	#endif
	
	return bytes_written;
}


int read_cframe(int fd, char cframe[]) {
	int state = F1_STATE;
	char c;
	
	
	if( _flag == SENDER ) {
		_alarm = 0;
		alarm(time_out);
	}
	
	#if DEBUG == 2
	printf("STATE1: %d\n", state);
	#endif

	while( state <= F2_STATE ) {
	
		do {		
			if( _flag == SENDER && _alarm == 1 ) {
				printf("CONNECTION TIMEOUT\n");
				return FRAME_RD_TIMEOUT;
			}
		
		} while( read(fd, &c, 1) <= 0 );

		#if DEBUG == 2
			printf("read_cframe(): BYTE TO READ: %x | %c\n", cframe[state], cframe[state]);
			printf("read_cframe(): BYTE    READ: %x | %c\n", c, c);
		#endif

		if( c != cframe[state] && cframe[state] != FLAG )
			state = F1_STATE;
		else ++state;

		#if DEBUG == 2
			printf("read_cframe(): STATE: %d\n", state);
		#endif
	}

	#if DEBUG == 2
	printf("read_cframe(): FRAME_READ\n");
	#endif

	return FRAME_READ;
}

int read_dframe(int fd, char *packet) {
	char *dframe_header = get_dframe_header();

	int state = F1_STATE;
	char c;
	
	#if DEBUG == 2
	printf("STATE1: %d\n", state);
	#endif

	while( state <= BCC_STATE ) {

		while( read(fd, &c, 1) <= 0 );
		
		#if DEBUG == 2
			printf("read_dframe(): BYTE TO READ: %x | %c\n", dframe_header[state], dframe_header[state]);
			printf("read_dframe(): BYTE    READ: %x | %c\n", c, c);
		#endif

		if( c != dframe_header[state] && dframe_header[state] != FLAG ) {
			#if DEBUG >= 1
			printf("FRAME_RD_ERROR\n");
			#endif
			return FRAME_RD_ERROR;
		}
		else ++state;

		#if DEBUG == 2
			printf("read_dframe(): STATE: %d\n", state);
		#endif
	}
	
	char buffer[MAX_FRAME_SIZE];
	int i = 0;
	while( c != FLAG ) {
		while( read(fd, &c, 1) <= 0 );
		buffer[i++] = c;
		if( i >= MAX_FRAME_SIZE ) {
			#if DEBUG >= 1
			printf("DATA FRAME EXCEEDS %x BYTES\n", MAX_FRAME_SIZE);
			#endif
			return FRAME_RD_ERROR;
		}
	}

	//apply destuff
	int destuff_packet_size = destuff_buffer(buffer, packet, --i) - 1;
	
	char bcc2_read = packet[destuff_packet_size], bcc2;
	
	for(bcc2 = packet[0], i = 1; i < destuff_packet_size; ++i) {
		bcc2 ^= packet[i];
	}

	if( bcc2 != bcc2_read ) {
		#if DEBUG == 2
		printf("BCC2 ERROR\n");
		#endif
		return FRAME_RD_ERROR;
	}
	
	#if DEBUG == 1
	printf("read_dframe(): FRAME_READ\n");
	#endif
	
	return FRAME_READ;
}


